package statistics

import (
	"context"
	"fmt"
	"gitee.com/jn-qq/simple-stress-testing/request"
	"sort"
	"strings"
	"sync"
	"time"
)

type Statistics struct {
	processingTime  int   // 处理总时间
	requestTime     int   // 请求总时间
	requestTimeList []int // 请求时间列表
	maxTime         int   // 最大时长
	minTime         int   // 最小时长
	successNum      int   // 成功处理数，code为0
	failureNum      int   // 处理失败数，code不为0
	chanIDLen       int   // 并发数
	chanIDs         map[int]bool
	receivedBytes   int
	codeMap         *sync.Map // 状态码个数
	mutex           sync.RWMutex
	customerNum     int // 线程数
}

func (s *Statistics) Receive(ctx context.Context, wg *sync.WaitGroup) {
	defer wg.Done()
	statTime := time.Now()
	var stopChan = make(chan bool)
	// 定时输出一次计算结果
	ticker := time.NewTicker(time.Second)
	go func() {
		for {
			select {
			case <-ticker.C:
				go s.calculateData(time.Since(statTime).Seconds())
			case <-stopChan:
				// 处理完成
				break
			}
		}
	}()
	s.printHeader()
	// 初始化数据
	s.codeMap = &sync.Map{}
	s.chanIDs = make(map[int]bool)

	for {
		select {
		case <-ctx.Done():
			goto end
		case data, ok := <-request.CH:
			if !ok {
				goto end
			}
			s.mutex.Lock()
			// fmt.Println("处理一条数据", data.ID, data.Time, data.IsSucceed, data.ErrCode)
			s.processingTime += data.Time
			if s.maxTime <= data.Time {
				s.maxTime = data.Time
			}
			if s.minTime == 0 {
				s.minTime = data.Time
			} else if s.minTime > data.Time {
				s.minTime = data.Time
			}
			// 是否请求成功
			if data.IsSucceed {
				s.successNum += 1
			} else {
				s.failureNum += 1
			}
			// 统计状态码
			if value, ok := s.codeMap.Load(data.StatusCode); ok {
				s.codeMap.Store(data.StatusCode, value.(int)+1)
			} else {
				s.codeMap.Store(data.StatusCode, 1)
			}
			// 返回字节长度
			s.receivedBytes += data.ReceivedBytes
			if _, ok := s.chanIDs[data.ChanID]; !ok {
				s.chanIDs[data.ChanID] = true
				s.chanIDLen = len(s.chanIDs)
			}
			s.requestTimeList = append(s.requestTimeList, data.Time)
			s.mutex.Unlock()
		}
	}
end:
	// 数据全部接受完成，停止定时输出统计数据
	stopChan <- true
	time.Sleep(1 * time.Second)
	requestTime := time.Since(statTime).Seconds()
	s.calculateData(requestTime)
	ChartObj.createPng()
	fmt.Printf("\n\n")
	fmt.Println("*************************  结果 stat  ****************************")
	fmt.Println("处理协程数量:", s.customerNum)
	fmt.Println("请求总数（并发数*请求数 -c * -n）:", s.successNum+s.failureNum, "\n总请求时间:",
		fmt.Sprintf("%.3f", requestTime), "秒", "\nsuccessNum:", s.successNum, "\nfailureNum:", s.failureNum)
	fmt.Println("*************************  结果 end   ****************************")
	fmt.Printf("\n\n")
}

func (s *Statistics) printHeader() {
	fmt.Printf("\n\n")
	// 打印的时长都为毫秒 总请数
	fmt.Println("───────────┬─────────┬────────────┬───────────┬─────────┬──────┬───────┬─────────────┬─────────────┬─────────────┬───────┬─────────┬────────────")
	fmt.Println(" COST_TIME │ PROCESS │ PROCESSING │ THREADING │ SUCCESS │ FAIL │  QPS  │ MAX_TIME/ms │ MIN_TIME/ms │ AVG_TIME/ms │ BYTES │ BYTES/s │ STATUS_CODE")
	fmt.Println("───────────┼─────────┼────────────┼───────────┼─────────┼──────┼───────┼─────────────┼─────────────┼─────────────┼───────┼─────────┼────────────")
}

// calculateData 计算数据
func (s *Statistics) calculateData(requestTime float64) {
	s.mutex.Lock()
	defer s.mutex.Unlock()

	if s.processingTime == 0 {
		s.processingTime = 1
	}
	var (
		qps              float64
		averageTime      float64
		maxTimeFloat     float64
		minTimeFloat     float64
		requestTimeFloat float64
	)
	// 平均 QPS 成功数*总协程数/总耗时 (每秒)
	qps = float64(s.successNum*s.customerNum) * (1e9 / float64(s.processingTime))

	// 平均时长 总耗时/总请求数/并发数 纳秒=>毫秒
	if s.successNum != 0 && s.customerNum != 0 {
		averageTime = float64(s.processingTime) / float64(s.successNum*1e6)
	}
	// 纳秒=>毫秒
	maxTimeFloat = float64(s.maxTime) / 1e6
	minTimeFloat = float64(s.minTime) / 1e6
	requestTimeFloat = requestTime
	// 打印的时长都为毫秒
	s.table(qps, averageTime, maxTimeFloat, minTimeFloat, requestTimeFloat)
	ChartObj.add(requestTime, map[string]float64{
		"PROCESSING": float64(request.ConcurrencyMap.CNGet()),
		"THREADING":  float64(request.ConcurrencyMap.TNGet()),
		"SUCCESS":    float64(s.successNum),
		"FAIL":       float64(s.failureNum),
		"QPS":        qps,
		"AVG_TIME":   averageTime,
	})
}

// table 打印表格
func (s *Statistics) table(qps, averageTime, maxTimeFloat, minTimeFloat, requestTimeFloat float64) {

	// 每秒处理字节数
	var speed int
	if requestTimeFloat > 0 {
		speed = int(float64(s.receivedBytes) / requestTimeFloat)
	} else {
		speed = 0
	}

	var (
		receivedBytesStr string
		speedStr         string
	)
	// 判断获取下载字节长度是否是未知
	if s.receivedBytes <= 0 {
		receivedBytesStr = ""
		speedStr = ""
	} else {
		receivedBytesStr = fmt.Sprintf("%d", s.receivedBytes)
		speedStr = fmt.Sprintf("%d", speed)
	}

	// 各返回码状态统计
	var codeStrList []string
	s.codeMap.Range(func(key, value interface{}) bool {
		codeStrList = append(codeStrList, fmt.Sprintf("%v:%v", key, value))
		return true
	})
	sort.Strings(codeStrList)

	// 打印的时长都为毫秒
	result := fmt.Sprintf(
		"%10.0fs│%9d│%12d│%11d│%9d│%6d│%7.2f│%13.2f│%13.2f│%13.2f│%7s│%9s│%v",
		requestTimeFloat, s.chanIDLen, request.ConcurrencyMap.CNGet(), request.ConcurrencyMap.TNGet(),
		s.successNum, s.failureNum, qps, maxTimeFloat, minTimeFloat, averageTime,
		receivedBytesStr, speedStr,
		strings.Join(codeStrList, "; "))
	fmt.Println(result)
}

func (s *Statistics) AddCustomerNum(n int) {
	s.mutex.Lock()
	defer s.mutex.Unlock()
	s.customerNum += n
}
